package ru.batrdmi.powermouse;

import com.intellij.ide.DataManager;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.ui.customization.CustomActionsSchema;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.util.Disposer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;

@State(name = "PowerMouse", storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml")})
public class EventDispatcher implements ApplicationComponent, IdeEventQueue.EventDispatcher, Disposable,
        PersistentStateComponent<EventDispatcher.Settings> {
    private static final long DELAY = 1000;

    private Settings settings = new Settings();
    private long popupMenuSuppressedTimestamp;
    private long powerClickTimestamp;

    public void initComponent() {
        IdeEventQueue.getInstance().addDispatcher(this, this);
        EditorFactory.getInstance().addEditorFactoryListener(new EditorFactoryAdapter() {
            @Override
            public void editorCreated(@NotNull EditorFactoryEvent event) {
                event.getEditor().addEditorMouseListener(new MyEditorMouseAdapter());
            }
        }, this);
    }

    public void disposeComponent() {
        Disposer.dispose(this);
    }

    @Override
    public void dispose() {
    }

    @NotNull
    public String getComponentName() {
        return "PowerMouse.EventDispatcher";
    }

    @Override
    public boolean dispatch(AWTEvent e) {
        if (!(e instanceof MouseEvent)) {
            return false;
        }
        MouseEvent me = (MouseEvent) e;
        if (((me.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) == 0 || me.getButton() != MouseEvent.BUTTON3)
                && ((me.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) == 0 || me.getButton() != MouseEvent.BUTTON1)) {
            return false;
        }
        if (me.getID() == MouseEvent.MOUSE_PRESSED) {
            if (runAction(me)) {
                powerClickTimestamp = me.getWhen();
            }
        }
        return true;
    }

    private boolean runAction(MouseEvent me) {
        String actionId = settings.actionIdToExecuteOnPowerClick;
        if (actionId == null) {
            return false;
        }
        ActionManager actionManager = ActionManager.getInstance();
        Component component = SwingUtilities.getDeepestComponentAt(me.getComponent(), me.getX(), me.getY());
        AnAction action = actionManager.getAction(actionId);
        if (action == null) {
            return false;
        }
        Presentation presentation = action.getTemplatePresentation().clone();
        DataContext dataContext = DataManager.getInstance().getDataContext(component);
        AnActionEvent e = new AnActionEvent(me, dataContext, ActionPlaces.MAIN_MENU, presentation, actionManager, me.getModifiers());
        action.update(e);
        if (e.getPresentation().isEnabled()) {
            action.actionPerformed(e);
            return true;
        } else {
            return false;
        }
    }

    private void showEditorPopupMenu(MouseEvent me) {
        ActionGroup group = (ActionGroup) CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_EDITOR_POPUP);
        ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.EDITOR_POPUP, group);
        final Component c = me.getComponent();
        if (c != null && c.isShowing()) {
            popupMenu.getComponent().show(c, me.getX(), me.getY());
        }
    }

    public static Settings getSettings() {
        return ApplicationManager.getApplication().getComponent(EventDispatcher.class).getState();
    }

    @Nullable
    @Override
    public Settings getState() {
        return settings;
    }

    @Override
    public void loadState(Settings settings) {
        this.settings = settings;
    }

    private class MyEditorMouseAdapter extends EditorMouseAdapter {
        @Override
        public void mousePressed(EditorMouseEvent e) {
            MouseEvent me = e.getMouseEvent();
            if (me.getButton() == MouseEvent.BUTTON3
                    && me.isPopupTrigger()
                    && e.getArea() == EditorMouseEventArea.EDITING_AREA) {
                popupMenuSuppressedTimestamp = me.getWhen();
                e.consume();
            }
        }

        @Override
        public void mouseReleased(EditorMouseEvent e) {
            MouseEvent me = e.getMouseEvent();
            if (me.getButton() == MouseEvent.BUTTON3 && e.getArea() == EditorMouseEventArea.EDITING_AREA) {
                if (me.getWhen() - powerClickTimestamp > DELAY) {
                    if (me.getWhen() - popupMenuSuppressedTimestamp < DELAY) {
                        showEditorPopupMenu(me);
                    }
                } else {
                    if (me.isPopupTrigger()) {
                        e.consume();
                    }
                }
            }
        }
    }

    public static class Settings {
        public String actionIdToExecuteOnPowerClick = "ShowUsages";
    }
}
